package bma.common.thrift.xdom.impl;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.apache.thrift.protocol.TType;

import bma.common.langutil.xdom.Xdom;
import bma.common.thrift.xdom.DynamicThriftManager;
import bma.common.thrift.xdom.DynamicThriftUtil;
import bma.common.thrift.xdom.TDynamicEnum;
import bma.common.thrift.xdom.TDynamicField;
import bma.common.thrift.xdom.TDynamicFunction;
import bma.common.thrift.xdom.TDynamicStruct;
import bma.common.thrift.xdom.TDynamicType;

public class DynamicThriftManagerSimple implements DynamicThriftManager {

	private Map<String, TDynamicFunction> functions;
	private Map<String, TDynamicStruct> structs;
	private Map<String, TDynamicEnum> enums;

	public Map<String, TDynamicFunction> getFunctions() {
		return functions;
	}

	public void setFunctions(Map<String, TDynamicFunction> functions) {
		this.functions = functions;
	}

	public void addFunction(TDynamicFunction f) {
		if (this.functions == null) {
			this.functions = new HashMap<String, TDynamicFunction>();
		}
		this.functions.put(f.getName(), f);
	}

	public Map<String, TDynamicStruct> getStructs() {
		return structs;
	}

	public void setStructs(Map<String, TDynamicStruct> structs) {
		this.structs = structs;
	}

	public void addStruct(TDynamicStruct s) {
		if (this.structs == null) {
			this.structs = new HashMap<String, TDynamicStruct>();
		}
		this.structs.put(s.getName(), s);
	}

	public Map<String, TDynamicEnum> getEnums() {
		return enums;
	}

	public void setEnums(Map<String, TDynamicEnum> enums) {
		this.enums = enums;
	}

	public void addEnum(TDynamicEnum e) {
		if (this.enums == null) {
			this.enums = new HashMap<String, TDynamicEnum>();
		}
		this.enums.put(e.getName(), e);
	}

	@Override
	public TDynamicFunction getFunction(String name) {
		if (this.functions != null) {
			return this.functions.get(name);
		}
		return null;
	}

	@Override
	public TDynamicStruct getStruct(String name) {
		if (this.structs != null) {
			return this.structs.get(name);
		}
		return null;
	}

	@Override
	public int getEnumValue(String enumName, String enumString) {
		if (this.enums != null) {
			TDynamicEnum e = this.enums.get(enumName);
			if (e != null) {
				return e.get(enumString);
			}
		}
		return -1;
	}

	public void init(Xdom desc) {
		if (desc == null)
			return;
		Xdom denums = desc.get("enum");
		Set<String> ns = initEnum(denums);
		Xdom dstructs = desc.get("struct");
		initStruct(dstructs, ns);
		Xdom dservices = desc.get("service");
		initService(dservices, ns);
	}

	public Set<String> initEnum(Xdom denums) {
		Set<String> r = new HashSet<String>();
		if (denums == null)
			return r;
		Iterator<String> names = denums.mapNames();
		if(names==null)return r;
		while (names.hasNext()) {
			String n = names.next();
			TDynamicEnum o = new TDynamicEnum();
			o.setName(n);

			Xdom denum = denums.get(n);
			Iterator<Xdom> it2 = denum.arrayIterator();
			while (it2.hasNext()) {
				Xdom e = it2.next();
				Xdom key = e.get("name");
				Xdom val = e.get("value");
				if (key != null && key.isString() && val != null
						&& val.isNumber()) {
					o.add(key.getStringValue(), val.getIntValue());
				}
			}
			addEnum(o);
			r.add(n);
		}
		return r;
	}

	public void initStruct(Xdom dstructs, Set<String> enums) {
		if (dstructs == null)
			return;
		Iterator<String> names = dstructs.mapNames();
		if(names==null)return;
		while (names.hasNext()) {
			String n = names.next();
			Xdom dstruct = dstructs.get(n);
			TDynamicStruct s = new TDynamicStruct();
			s.setName(n);
			s.setCallArgs(false);
			s.setCallReturn(false);

			Xdom dmembers = dstruct.get("members");
			if (dmembers != null) {
				Iterator<Xdom> it2 = dmembers.arrayIterator();
				if(it2==null)continue;
				while (it2.hasNext()) {
					Xdom e = it2.next();
					TDynamicField field = createField(e, enums);
					s.addField(field);
				}
			}
			addStruct(s);
		}
	}
	
	public void initService(Xdom dservices, Set<String> enums) {
		if (dservices == null)
			return;
		Iterator<String> names = dservices.mapNames();
		if(names==null)return;
		while (names.hasNext()) {
			String n = names.next();
			Xdom dservice = dservices.get(n);
			Iterator<String> fnames = dservice.mapNames();
			if(fnames==null)continue;
			while(fnames.hasNext()) {
				String fname = fnames.next();
				Xdom dfun = dservice.get(fname);
				
				TDynamicFunction f = new TDynamicFunction();				
				f.setName(fname);
				f.setManager(this);
				Xdom dom = dfun.get("oneway");
				if(dom!=null) {
					f.setOneway(dom.getBooleanValue());
				}
				
				Xdom dparams = dfun.get("args");
				if (dparams != null) {
					Iterator<Xdom> it2 = dparams.arrayIterator();
					if(it2==null)continue;
					while (it2.hasNext()) {
						Xdom e = it2.next();
						TDynamicField field = createField(e, enums);
						f.addParam(field);
					}
				}
				
				Xdom dret = dfun.get("return");
				TDynamicType type = createType(dret, enums);
				TDynamicField success = new TDynamicField();
				success.setKey(0);
				success.setName("success");
				success.setType(type);
				
				TDynamicStruct sret = new TDynamicStruct();
				sret.setName(fname+"_result");
				sret.setCallArgs(false);
				sret.setCallReturn(true);
				sret.addField(success);
				
				f.setReturnStruct(sret);
				
				addFunction(f);
			}
		}
	}

	public static TDynamicField createField(Xdom dfield, Set<String> enums) {
		TDynamicField r = new TDynamicField();
		Xdom v;
		v = dfield.get("name");
		if (v != null && v.isString()) {
			r.setName(v.getStringValue());
		}
		v = dfield.get("key");
		if (v != null && v.isNumber()) {
			r.setKey(v.getIntValue());
		}
		v = dfield.get("type");
		TDynamicType type = createType(v, enums);
		r.setType(type);
		v = dfield.get("optional");
		if (v != null && v.isBoolean()) {
			r.setOptional(v.getBooleanValue());
		}
		return r;
	}

	public static TDynamicType createType(Xdom v, Set<String> enums) {
		TDynamicType r = new TDynamicType();
		r.setType(TType.VOID);
		r.setName("void");
		
		if (v == null) {
			return r;
		}
		if (v.isString()) {
			String tname = v.getStringValue();
			byte t = DynamicThriftUtil.ttype(tname);
			if(t==TType.STOP) {
				if(enums!=null && enums.contains(tname)) {
					t = TType.ENUM;
				} else {
					t = TType.STRUCT;
				}
			}
			r.setName(tname);
			r.setType(t);			
		}
		if(v.isMap()) {
			Xdom dom = v.get("name");
			String tname = "";
			if(dom!=null && dom.isString()) {
				tname = dom.getStringValue();
			}
			byte t = DynamicThriftUtil.ttype(tname);
			dom = v.get("inner");
			if(dom!=null && !dom.isNull()) {
				TDynamicType innerType = createType(dom, enums);
				r.setInnerType(innerType);
			}
			dom = v.get("key");
			if(dom!=null && !dom.isNull()) {
				TDynamicType keyType = createType(dom, enums);
				r.setKeyType(keyType);
			}			
			r.setName(tname);
			r.setType(t);
		}
		return r;
	}

}
